package com.yuhongwei.javaproject.address;

import com.yuhongwei.javaproject.tools.Base58;
import com.yuhongwei.javaproject.tools.Hash;

import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.util.Arrays;

/**
 * @author: jiangjiwei
 * @email: jiangjiwei@3452788854qq.com
 * @desc: 该类用于推导生成比特币格式地址，并执行校验
 * @datetime: 2021/12/28 19:27
 */
public class Address {
    /**
     *  该方法用于生成一对密钥对，并将该密钥对返回
     *
     * @return  生成的密钥对
     */
     public KeyPair generateKye(){

         try {
             KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
              // secp256k1、secp256r1

             ECGenParameterSpec spec = new ECGenParameterSpec("secp256k1");
           generator.initialize(spec);
           return generator.genKeyPair();

         } catch (NoSuchAlgorithmException e) {
             e.printStackTrace();
         } catch (InvalidAlgorithmParameterException e) {
             e.printStackTrace();
         }

     return null;
     }

    /**
     *   该方法用于根据提供的keypair 密钥信息，推导原始格式地址
     * @param keyPair 密钥信息
     * @return 生成的原始格式地址
     */
     public String newAdderss(KeyPair keyPair){
        ECPublicKey  publicKey = (ECPublicKey) keyPair.getPublic();
        // 自己进行原始格式公钥的拼接
       byte[] pubKey = toUnCompressedPublicKey(publicKey);

       // 对公钥进行hash计算
         byte[] sha256 = Hash.SHA256(pubKey);
         // ripemd160
        byte[] pubhash = Hash.RipeMD160(sha256);

        // 版本号
         byte[] version = new  byte[1];
         version[0] = 0x00;

        // 添加版本号
         byte[] ver_pubhash = new  byte[version.length + pubhash.length];
         System.arraycopy(version,0,ver_pubhash,0,version.length);
         System.arraycopy(pubhash,0,ver_pubhash,1,pubhash.length);

                  // 双hash，取前4个字节
            byte[] hash1 = Hash.SHA256(ver_pubhash);
            byte[] hash2 = Hash.SHA256(hash1);

            // 校验为数组
         byte[] check = new byte[4];
         System.arraycopy(hash2,0,check,0,check.length);

         // 申请 ver_pubhash_check 数组
         byte[] ver_pubhash_check = new byte[ver_pubhash.length+check.length];
         System.arraycopy(ver_pubhash,0,ver_pubhash_check,0,ver_pubhash.length);
         System.arraycopy(check,0,ver_pubhash_check,ver_pubhash.length,check.length);

         // Base58编码
       return   Base58.encode(ver_pubhash_check);

     }

    /**
     *  用于校验地址是否符合规范，并返回校验结果
     * @param addr 要校验的地址
     * @return 检验结果true表示符合，flase表示不符合规范
     */
     public boolean isValidate(String addr){
         if("". equals(addr) || addr == null){
             return false;
         }
         // 1.base58解码
         byte[] ver_pubhash_check = Base58.decode(addr);
        if (ver_pubhash_check.length <4){

            return false;
        }
         // 2. 申请存储检验码的数组空间
         byte[] check = new byte[4];
         System.arraycopy(ver_pubhash_check,ver_pubhash_check.length-4,check,0,check.length);

         byte[] ver_pubhash = new byte[ver_pubhash_check.length-check.length];
         System.arraycopy(ver_pubhash_check,0,ver_pubhash,0,ver_pubhash.length);

         // 双hash
         byte[] hash1 = Hash.SHA256(ver_pubhash);
         byte[] hash2 = Hash.SHA256(hash1);

         byte[] check_sum = new byte[4];
         System.arraycopy(hash2,0,check_sum,0,check_sum.length);

        return Arrays.equals(check,check_sum);

     }

    /**
     *  该方法用于拼接得到原始格式的公钥
     * @return 原始公钥字节数组
     */
      public byte[] toUnCompressedPublicKey(ECPublicKey publicKey){
          ECPoint point = publicKey.getW();
          byte[] xBytes = point.getAffineX().toByteArray();
          byte[] yBytes = point.getAffineX().toByteArray();

          // 申请65字节大小的字节数组
          byte[] puKey = new  byte[1+2*32];
          puKey[0] = 04;


          // x坐标
          if (xBytes.length == 32){
              System.arraycopy(xBytes,0,puKey,1,xBytes.length);
          }else if (xBytes.length >32){
              System.arraycopy(xBytes,1,puKey,1,32);
          }

          // y坐标
          if (xBytes.length == 32){
              System.arraycopy(yBytes,0,puKey,33,yBytes.length);
          }else  if (yBytes.length > 32){
              System.arraycopy(yBytes,1,puKey,33,32);
          }
          // 将65字节的数据返回
          return puKey;


      }


    // 暂时测试使用的main函数
    public static void main(String[] args) {
        Address addr = new Address();
        KeyPair keyPair = addr.generateKye();
     String addStr =   addr.newAdderss(keyPair);
     boolean result = addr.isValidate(addStr);
     System.out.println(result);
    }

}
